Get Data

We’ll load the raw data from all 8 .xls files. Each file shows the participant’s name, group, language, media, and x/y eye gaze coordinates. Here’s a quick glimpse of the data and how it’s structured after I’ve processed and cleaned it up.

# Import packages we'll need.
library(tidyverse)
library(stringr)
library(lme4)
library(lmerTest)
library(png)
library(grid)
# # Gather up the files
# files <- list.files(pattern = "\\.csv",path="../Adult Data/rawdata")
# files <- str_c("rawdata/",files)
# rawdata <- do.call("rbind", lapply(files, read_csv))
# 
# # Clean Up
# rawdataoriginal <- rawdata
# rawdata <- rawdataoriginal
# rawdata <- rawdata %>%
#   rename(participant = ParticipantName,
#          group = "[Group]Value",
#          language = "[Language]Value",
#          media = MediaName,
#          x = "GazePointX (MCSpx)",
#          y = "GazePointY (MCSpx)",
#          gazeindex = GazePointIndex) %>%
#   select(participant,group,language,media,x,y,gazeindex) %>%
#   add_column(story=NA,direction=NA,maingroup=NA) %>%
#   mutate(story = case_when(
#     str_detect(media,"bears") ~ "bears",
#     str_detect(media,"cinderella") ~ "cinderella",   
#     str_detect(media,"midas") ~ "midas",
#     str_detect(media,"redridinghood") ~ "redridinghood")) %>%
#   mutate(direction = case_when(
#     str_detect(media,"FW") ~ "forward",
#     str_detect(media,"ER") ~ "reversed")) %>%
#   mutate(media = str_c(story,direction, sep="_")) %>%
#   mutate(language = case_when(
#     str_detect(language,"EarlyASL") ~ "Early",
#     str_detect(language,"LateASL") ~ "Late",
#     str_detect(language,"NoviceASL_Trained") ~ "Trained",
#     str_detect(language,"NoviceASL") ~ "Novice")) %>%
#   mutate(maingroup = str_c(group,language, sep="")) %>%
#   filter(!is.na(maingroup))
# glimpse(rawdata)

Now that it’s in the right format…it’s easy to get what we need! :-D

Analysis

First, let’s trim each participant’s data, getting rid of the first 30 samples (0.5 secs). Then we’ll get the the mean x and y coordinate for each story for each participant.

#write_csv(rawdata,"adultviewingspaceraw.csv")
rawdata <- read_csv("../Adult Data/rawdata/adultviewingspaceraw.csv")
Parsed with column specification:
cols(
  participant = col_character(),
  group = col_character(),
  language = col_character(),
  media = col_character(),
  x = col_integer(),
  y = col_integer(),
  gazeindex = col_integer(),
  story = col_character(),
  direction = col_character(),
  maingroup = col_character()
)
rawdata <- rawdata %>%
  arrange(participant,media,gazeindex) %>%
  group_by(participant,media) %>%
  slice(30:n())
means <- rawdata %>%
  group_by(participant,media) %>%
  summarise(x = mean(x,na.rm=TRUE),
            y = mean(y,na.rm=TRUE))
head(means,10)

And I can get x or y plots of one participant across 4 stories. Let’s do Adam (me?). We’ll set the x and y limits to the whole width of the Tobii monitor (1600x1200). But because Tobii considers (0,0) to be the upper left corner (and not the bottom left corner), we also need to flip the y axis.

adam <- filter(rawdata,participant=="Adam") %>% mutate(y = y*-1)
ggplot(adam,aes(x=x,y=y,color=media)) + geom_point(size=0.1) + geom_path() + facet_wrap("media",ncol=2,nrow=2) + guides(color="none") + scale_x_continuous(limit=c(0,1600)) + scale_y_continuous(limit=c(-1200,0))

Cool, yeah?

Let’s try this again but let the x and y limits match the data. That will “zoom” in. We’ll also get rid of that weird right-side outlier in RRH. Neatooooo.

adam <- filter(adam,x<800)
ggplot(adam,aes(x=x,y=y,color=media)) + geom_point(size=0.1) + geom_path() + facet_wrap("media",ncol=2,nrow=2) + guides(color="none") 

IQR

Now let’s get the middle 50% (aka the IQR) of x and y for each participant’s story (we’ve already trimmed the first 30 samples). That should also take care of further weird outliers. And we are defining “viewing space” as the IQR of the x and y axis.

iqr <- rawdata %>%
  group_by(participant,media) %>%
  dplyr::summarize(xIQR = IQR(x,na.rm=TRUE),
                   yIQR = IQR(y,na.rm=TRUE),
                   xmed = median(x, na.rm=TRUE),
                   ymed = median(y, na.rm=TRUE))
head(iqr,10)

And check out the histograms:

iqr %>% 
  gather(axis,iqr,xIQR:yIQR) %>%
  ggplot(aes(x=iqr,fill=axis)) + geom_histogram() + facet_grid(axis~.)

There’s one weird outlier for xIQR. That’s Rebecca. Let’s look at her data.

rebecca <- filter(rawdata,participant=="Rebecca") %>% mutate(y = y*-1)
ggplot(rebecca,aes(x=x,y=y,color=media)) + geom_point(size=0.1) + geom_path() + facet_wrap("media",ncol=2,nrow=2) + guides(color="none") + xlim(0,1440) + ylim(-1080,0)

So we’ll remove Rebecca’s midas_forward story. Next, check the medians.

iqr %>% 
  gather(axis,med,xmed:ymed) %>%
  ggplot(aes(x=med,fill=axis)) + geom_histogram() + facet_grid(axis~.)

Looks great! But it also looks pretty interesting doesn’t it…Y median has a wider spread than X median. That would make sense.

Now we’re ready to do stats based on group, direction, etc.

rbc <- tribble(~participant, ~media, "Rebecca","midas_forward")
iqr <- iqr %>%
  ungroup() %>%
  anti_join(rbc, by=c("participant","media")) # for some reason filter wouldn't work
subjectinfo <- rawdata %>%
  select(participant,maingroup,group,language,media,story,direction) %>%
  distinct() %>%
  filter(!is.na(participant))
iqr <- iqr %>%
  left_join(subjectinfo,by=c("participant","media")) %>%
  filter(!is.na(maingroup))
iqr.gather <- iqr %>% gather(axis,value,xIQR:ymed)
iqr.iqr <- filter(iqr.gather,axis=="xIQR" | axis=="yIQR")
iqr.med <- filter(iqr.gather,axis=="xmed" | axis=="ymed")
ggplot(iqr.iqr,aes(x=maingroup,y=value,fill=direction)) + 
  geom_boxplot() + theme(axis.text.x=element_text(angle=45,hjust=1)) +
  facet_grid(.~axis)

And the median x and y position (this assumes all calibrations are correct):

ggplot(iqr.med,aes(x=maingroup,y=value,fill=direction)) + 
  geom_boxplot() + theme(axis.text.x=element_text(angle=45,hjust=1)) +
  facet_grid(.~axis)

First, does reversal have an effect on X IQR? We have random intercepts for each participant and media, and a random slope adjustment for reversed for each participant.

xiqr.reversal <- lmer(xIQR ~ direction + (direction|participant) + (1|media), data = iqr)
summary(xiqr.reversal)$coefficients
                   Estimate Std. Error       df   t value     Pr(>|t|)
(Intercept)       37.067311   4.297525 5.879626 8.6252701 0.0001491978
directionreversed  2.396059   5.922401 5.288700 0.4045755 0.7016363535

Welp. No. Y IQR?

yiqr.reversal <- lmer(yIQR ~ direction + (direction|participant) + (1|media), data = iqr)
summary(yiqr.reversal)$coefficients
                   Estimate Std. Error       df    t value     Pr(>|t|)
(Intercept)       46.043362   4.200520 6.970763 10.9613480 1.200581e-05
directionreversed  4.845994   5.370947 4.578010  0.9022605 4.118721e-01

No effect here either. Let’s try adding maingroups. X IQR first.

xiqr.group <- lmer(xIQR ~ direction * maingroup + (direction|participant) + (1|media), data = iqr)
summary(xiqr.group)$coefficients
                                            Estimate Std. Error         df     t value     Pr(>|t|)
(Intercept)                               34.6927610   4.977661   9.371920  6.96969146 5.346237e-05
directionreversed                          6.1771409   6.649067   7.438029  0.92902372 3.820371e-01
maingroupDeafLate                         -3.1765664   4.967385  78.798842 -0.63948467 5.243625e-01
maingroupHearingEarly                     -0.4499422   7.743459  73.051139 -0.05810609 9.538228e-01
maingroupHearingLate                       7.1504388   4.359923  75.858667  1.64003778 1.051379e-01
maingroupHearingNovice                     1.7218180   4.676980  74.285758  0.36814740 7.138103e-01
maingroupHearingTrained                    6.1822390   4.333229  74.566382  1.42670499 1.578388e-01
directionreversed:maingroupDeafLate       -5.3237257   5.606349 109.885316 -0.94958867 3.444051e-01
directionreversed:maingroupHearingEarly   -5.7646039   8.485478 106.947583 -0.67934934 4.983842e-01
directionreversed:maingroupHearingLate    -7.9499453   4.850258 110.002650 -1.63907694 1.040542e-01
directionreversed:maingroupHearingNovice  -4.5441561   5.174925 107.927462 -0.87811047 3.818344e-01
directionreversed:maingroupHearingTrained -2.8665334   4.835685 109.082262 -0.59278748 5.545503e-01

This means there is a significant difference of DeafEarly vs. HearingLate and HearingTrained on xIQR, but in the forward condition only. Basically, those two groups have a “wider” viewing space than other groups.

yiqr.group <- lmer(yIQR ~ direction * maingroup + (direction|participant) + (1|media), data = iqr)
summary(yiqr.group)$coefficients
                                            Estimate Std. Error         df     t value     Pr(>|t|)
(Intercept)                               43.6712994   5.694180  22.801965  7.66946239 9.299788e-08
directionreversed                          2.2511171   6.477516   9.643778  0.34752785 7.356598e-01
maingroupDeafLate                          0.5128043   8.350353  72.818563  0.06141110 9.512001e-01
maingroupHearingEarly                     -4.5089659  13.105397  67.201619 -0.34405412 7.318798e-01
maingroupHearingLate                       0.6217192   7.364000  69.503009  0.08442684 9.329598e-01
maingroupHearingNovice                     4.4728826   7.906578  68.286947  0.56571661 5.734407e-01
maingroupHearingTrained                    8.7840578   7.325681  68.444490  1.19907732 2.346327e-01
directionreversed:maingroupDeafLate       -6.0754371   8.026624 142.796568 -0.75691059 4.503505e-01
directionreversed:maingroupHearingEarly    1.0060946  12.144589 140.217132  0.08284304 9.340945e-01
directionreversed:maingroupHearingLate     4.4331711   6.951655 142.667852  0.63771449 5.246817e-01
directionreversed:maingroupHearingNovice   8.5502379   7.409162 140.930045  1.15400881 2.504498e-01
directionreversed:maingroupHearingTrained  5.8176819   6.927937 141.706193  0.83974235 4.024674e-01

No differences among groups or reversal effect for xIQR. Viewing space doesn’t get significantly taller or shorter.

Viewing Space Charts

I want to learn how to make rectangle plots so here we go. Using each participant’s four x and y medians and 4 x and y IQRs (one set for each story, for 4 stories). So I can get the logic and code down. Let’s assume all calibrations were correct. Here’s the chart for the whole media size of 1440x1080 (as reported in Tobii).

# In this order, we'll get a grand median by taking a participant's median across their 4 stories, than the median for forward and reverse across all participants. 
medians <- iqr %>%
  group_by(maingroup, participant,direction) %>%
  dplyr::summarize(xIQR = median(xIQR,na.rm=TRUE),
                   yIQR = median(yIQR,na.rm=TRUE),
                   xmed = median(xmed,na.rm=TRUE),
                   ymed = median(ymed,na.rm=TRUE)) %>%
  group_by(maingroup, direction) %>% 
  dplyr::summarize(xIQR = median(xIQR,na.rm=TRUE),
                   yIQR = median(yIQR,na.rm=TRUE),
                   x = median(xmed,na.rm=TRUE),
                   y = median(ymed,na.rm=TRUE))
medians <- medians %>%
  mutate(y = y*-1,
         xmin = x-(xIQR/2),
         xmax = x+(xIQR/2),
         ymin = y-(yIQR/2),
         ymax = y+(yIQR/2))
img <- readPNG("cindy.png")
g <- rasterGrob(img, interpolate=TRUE, width=unit(1,"npc"), height=unit(1,"npc")) 
ggplot(medians, aes(fill=direction,color=direction)) +
  annotation_custom(g, xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf) +
  geom_rect(aes(xmin=xmin,ymin=ymin,xmax=xmax,ymax=ymax),alpha=.1) + 
  theme_linedraw() + 
  scale_x_continuous(limits = c(0,1440), expand = c(0, 0)) +
  scale_y_continuous(limits = c(-1080,0), expand = c(0, 0)) +
  facet_wrap("maingroup")

# ggplot(iqr.global, aes(fill=direction,color=direction)) +
#   annotation_custom(g, xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf) +
#   geom_rect(aes(xmin=xmin,ymin=ymin,xmax=xmax,ymax=ymax),alpha=.1) + 
#   theme_minimal() + xlim(0,1440) + ylim(-1080,0) +
#   geom_hline(yintercept=-1080+885) +
#   geom_hline(yintercept=-1080+525) + 
#   annotate(geom="text", x = 300, y = -1080+555, label = "upper shoulder point") +
#   annotate(geom="point", x = 535, y = -1080+525) + 
#   annotate(geom="text", x = 535, y = -1080+910, label = "height line") + 
#   annotate(geom="rect", xmin = 535, xmax = 535+365, ymin = -525-551, ymax = -1080+525, fill="maroon", color="black", alpha=0.5) + 
#   annotate(geom="text", x = 700, y = -900, label = "torso")

Yayyy! It worked! A bit hard to see! Let’s zoom in.

# ggplot(iqr.global, aes(fill=direction,color=direction)) +
#   annotation_custom(g, xmin=0, xmax=1440, ymin=-1080, ymax=0) +
#   geom_rect(aes(xmin=xmin,ymin=ymin,xmax=xmax,ymax=ymax),alpha=.1) + 
#   theme_minimal() + xlim(600,800) + ylim(-500,-300)
ggplot(medians, aes(fill=direction,color=direction)) +
  geom_rect(aes(xmin=xmin,ymin=ymin,xmax=xmax,ymax=ymax),alpha=.1) + 
  theme_minimal() + xlim(677,743) + ylim(-421,-370)

#ggplot(iqr.global, aes(fill=direction,color=direction)) +
#  geom_rect(aes(xmin=xmin,ymin=ymin,xmax=xmax,ymax=ymax),alpha=.1)

I have to figure out how to get Cindy to zoom in - or crop a zoomed in version of Cindy manually and use that.

Viewing Space Charts for Individuals

Now let’s see the variation in viewing spaces for all our individuals. Should be fun.

iqr.individuals <- iqr %>%
  rename(x = xmed,
         y = ymed) %>%
  mutate(y = y*-1,
         xmin = x-(xIQR/2),
         xmax = x+(xIQR/2),
         ymin = y-(yIQR/2),
         ymax = y+(yIQR/2))
ggplot(iqr.individuals, aes(fill=direction,color=direction)) +
  annotation_custom(g, xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf) +
  geom_rect(aes(xmin=xmin,ymin=ymin,xmax=xmax,ymax=ymax),alpha=.1) + 
  theme_minimal() + xlim(0,1440) + ylim(-1080,0) + facet_wrap("direction") +
  ggtitle("with IQRs")

Standard Deviations?

Let’s try using standard deviations instead.

sd <- rawdata %>%
  group_by(participant,media) %>%
  dplyr::summarize(xsd = sd(x,na.rm=TRUE),
                   ysd = sd(y,na.rm=TRUE),
                   xmean = mean(x, na.rm=TRUE),
                   ymean = mean(y, na.rm=TRUE))
sd.individuals <- sd %>%
  rename(x = xmean,
         y = ymean) %>%
  mutate(y = y*-1,
         xmin = x-xsd,
         xmax = x+xsd,
         ymin = y-ysd,
         ymax = y+ysd) %>%
  left_join(subjectinfo,by=c("participant","media")) %>%
  filter(!is.na(maingroup))
ggplot(sd.individuals, aes(fill=direction,color=direction)) +
  annotation_custom(g, xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf) +
  geom_rect(aes(xmin=xmin,ymin=ymin,xmax=xmax,ymax=ymax),alpha=.1) + 
  theme_minimal() + xlim(0,1440) + ylim(-1080,0) + facet_wrap("direction") +
  ggtitle("with SDs")

Now let’s make Outer Limits charts which is IQR +/- 2 SDs.

sd.individuals <- select(sd.individuals,participant,media,xsd,ysd)
iqrsd.individuals <- left_join(iqr.individuals,sd.individuals,by=c("participant","media")) %>%
  mutate(xmin = xmin-(2*xsd),
         xmax = xmax+(2*xsd),
         ymin = ymin-(2*ysd),
         ymax = ymax+(2*ysd))
ggplot(iqrsd.individuals, aes(fill=direction,color=direction)) +
  annotation_custom(g, xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf) +
  geom_rect(aes(xmin=xmin,ymin=ymin,xmax=xmax,ymax=ymax),alpha=.1) + 
  theme_minimal() + xlim(0,1440) + ylim(-1080,0) + facet_wrap("direction") +
  ggtitle("with SDs")

iqrsd.individuals <- iqrsd.individuals %>%
  group_by(direction) %>%
  dplyr::summarize(x = mean(x,na.rm=TRUE),
            y = mean(y,na.rm=TRUE),
            xmin = mean(xmin,na.rm=TRUE),
            ymin = mean(ymin,na.rm=TRUE),
            xmax = mean(xmax,na.rm=TRUE),
            ymax = mean(ymax,na.rm=TRUE))
ggplot(iqrsd.individuals, aes(fill=direction,color=direction)) +
  annotation_custom(g, xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf) +
  geom_rect(aes(xmin=xmin,ymin=ymin,xmax=xmax,ymax=ymax),alpha=.1) + 
  theme_minimal() + xlim(0,1440) + ylim(-1080,0) + facet_wrap("direction") +
  ggtitle("Average of above chart (rain's outer limits)")

XY Space Data - Multiple Plots

First let’s prep the data.

multiples <- rawdata %>%
  filter(!is.na(x)) %>%
  filter(!is.na(y)) %>%
  rename(lang = maingroup,
         name = participant) %>%
  group_by(name, lang, direction, story) %>%
  summarise(xIQR = IQR(x,na.rm=TRUE),
            yIQR = IQR(y,na.rm=TRUE),
            xmed = median(x, na.rm=TRUE),
            ymed = median(y, na.rm=TRUE),
            area = xIQR*yIQR,
            x_90 = quantile(x, .95, na.rm=TRUE) - quantile(x, .05, na.rm=TRUE),
            y_90 = quantile(y, .95, na.rm=TRUE) - quantile(y, .05, na.rm=TRUE),
            area_90 = (x_90) * (y_90),
            x_mean = mean(x, na.rm = TRUE),
            y_mean = mean(y, na.rm = TRUE),
            x_sd = sd(x, na.rm = TRUE),
            y_sd = sd(y, na.rm = TRUE),
            x_1sd = (x_mean+x_sd) - (x_mean-x_sd),
            y_1sd = (y_mean+y_sd) - (y_mean-y_sd),
            area_1sd = x_1sd * y_1sd,
            x_2sd = (x_mean+(x_sd*2)) - (x_mean-(x_sd*2)),
            y_2sd = (y_mean+(y_sd*2)) - (y_mean-(y_sd*2)),
            area_2sd = x_2sd * y_2sd) %>%
  group_by(name, lang, direction) %>%
  summarise_if(is.double, funs(mean), na.rm = T) %>%
  group_by(lang, direction) %>%
  summarise_if(is.double, funs(mean), na.rm = T) %>%
  filter(lang == "DeafEarly" || lang == "HearingNovice")
img <- png::readPNG("cindy.png")
g <- grid::rasterGrob(img, interpolate=TRUE, width=unit(1,"npc"), height=unit(1,"npc")) 

IQR (Middle 50%)

Let’s see.

curr_data <- multiples %>% 
  ungroup() %>%
  select(lang, direction, xmed, ymed, xIQR, yIQR) %>%
  group_by(lang, direction) %>%
  summarise(xmin = xmed-(xIQR/2),
         xmax = xmed+(xIQR/2),
         ymin = -1*(ymed-(yIQR/2)),
         ymax = -1*(ymed+(yIQR/2)))
ggplot(curr_data, aes(fill=direction,color=direction)) +
  annotation_custom(g, xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf) +
  geom_rect(aes(xmin=xmin,ymin=ymin,xmax=xmax,ymax=ymax),alpha=.1) + 
  theme_linedraw() +
  scale_x_continuous(limits = c(0,1440), expand = c(0, 0)) +
  scale_y_continuous(limits = c(-1080,0), expand = c(0, 0)) +
  facet_wrap("lang")

Middle 90%

So I calculated the average median across, and the middle 90% of the data.

curr_data <- multiples %>% 
  ungroup() %>%
  select(lang, direction, xmed, ymed, x_90, y_90) %>%
  group_by(lang, direction) %>%
  summarise(xmin = xmed-(x_90/2),
         xmax = xmed+(x_90/2),
         ymin = -1*(ymed-(y_90/2)),
         ymax = -1*(ymed+(y_90/2)))
ggplot(curr_data, aes(fill=direction,color=direction)) +
  annotation_custom(g, xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf) +
  geom_rect(aes(xmin=xmin,ymin=ymin,xmax=xmax,ymax=ymax),alpha=.1) + 
  theme_linedraw() +
  scale_x_continuous(limits = c(0,1440), expand = c(0, 0)) +
  scale_y_continuous(limits = c(-1080,0), expand = c(0, 0)) +
  facet_wrap("lang")

±1 SD (Middle 68%)

So this is using the mean of the means, plus or minus one SD. This is equivalent to middle 68%.

curr_data <- multiples %>% 
  ungroup() %>%
  select(lang, direction, x_mean, y_mean, x_1sd, y_1sd) %>%
  group_by(lang, direction) %>%
  summarise(xmin = x_mean-(x_1sd/2),
         xmax = x_mean+(x_1sd/2),
         ymin = -1*(y_mean-(y_1sd/2)),
         ymax = -1*(y_mean+(y_1sd/2)))
ggplot(curr_data, aes(fill=direction,color=direction)) +
  annotation_custom(g, xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf) +
  geom_rect(aes(xmin=xmin,ymin=ymin,xmax=xmax,ymax=ymax),alpha=.1) + 
  theme_linedraw() +
  scale_x_continuous(limits = c(0,1440), expand = c(0, 0)) +
  scale_y_continuous(limits = c(-1080,0), expand = c(0, 0)) +
  facet_wrap("lang")

±2 SD (Middle 96%)

And this is using the mean of the means, plus or minus two SD. This is equivalent to middle 96%.

curr_data <- multiples %>% 
  ungroup() %>%
  select(lang, direction, x_mean, y_mean, x_2sd, y_2sd) %>%
  group_by(lang, direction) %>%
  summarise(xmin = x_mean-(x_2sd/2),
         xmax = x_mean+(x_2sd/2),
         ymin = -1*(y_mean-(y_2sd/2)),
         ymax = -1*(y_mean+(y_2sd/2)))
ggplot(curr_data, aes(fill=direction,color=direction)) +
  annotation_custom(g, xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf) +
  geom_rect(aes(xmin=xmin,ymin=ymin,xmax=xmax,ymax=ymax),alpha=.1) + 
  theme_linedraw() +
  scale_x_continuous(limits = c(0,1440), expand = c(0, 0)) +
  scale_y_continuous(limits = c(-1080,0), expand = c(0, 0)) +
  facet_wrap("lang")

LS0tCnRpdGxlOiAiVmlld2luZyBTcGFjZSAoc3R1ZHkxYWR1bHRzKSIKYXV0aG9yOiAiQWRhbSBTdG9uZSwgUGhEIiAKZGF0ZTogJ2ByIGZvcm1hdChTeXMuRGF0ZSgpLCAiJW0tJWQtJVkiKWAnCm91dHB1dDoKICBnaXRodWJfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAyCiAgaHRtbF9ub3RlYm9vazoKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgdGhlbWU6IHBhcGVyCiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAyCiAgICB0b2NfZmxvYXQ6IHllcwogICAgZGZfcHJpbnQ6IHBhZ2VkCi0tLQoKIyBHZXQgRGF0YQpXZSdsbCBsb2FkIHRoZSByYXcgZGF0YSBmcm9tIGFsbCA4IC54bHMgZmlsZXMuIEVhY2ggZmlsZSBzaG93cyB0aGUgcGFydGljaXBhbnQncyBuYW1lLCBncm91cCwgbGFuZ3VhZ2UsIG1lZGlhLCBhbmQgeC95IGV5ZSBnYXplIGNvb3JkaW5hdGVzLiBIZXJlJ3MgYSBxdWljayBnbGltcHNlIG9mIHRoZSBkYXRhIGFuZCBob3cgaXQncyBzdHJ1Y3R1cmVkIGFmdGVyIEkndmUgcHJvY2Vzc2VkIGFuZCBjbGVhbmVkIGl0IHVwLiAKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgSW1wb3J0IHBhY2thZ2VzIHdlJ2xsIG5lZWQuCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHN0cmluZ3IpCmxpYnJhcnkobG1lNCkKbGlicmFyeShsbWVyVGVzdCkKbGlicmFyeShwbmcpCmxpYnJhcnkoZ3JpZCkKCiMgIyBHYXRoZXIgdXAgdGhlIGZpbGVzCiMgZmlsZXMgPC0gbGlzdC5maWxlcyhwYXR0ZXJuID0gIlxcLmNzdiIscGF0aD0iLi4vQWR1bHQgRGF0YS9yYXdkYXRhIikKIyBmaWxlcyA8LSBzdHJfYygicmF3ZGF0YS8iLGZpbGVzKQojIHJhd2RhdGEgPC0gZG8uY2FsbCgicmJpbmQiLCBsYXBwbHkoZmlsZXMsIHJlYWRfY3N2KSkKIyAKIyAjIENsZWFuIFVwCiMgcmF3ZGF0YW9yaWdpbmFsIDwtIHJhd2RhdGEKIyByYXdkYXRhIDwtIHJhd2RhdGFvcmlnaW5hbAojIHJhd2RhdGEgPC0gcmF3ZGF0YSAlPiUKIyAgIHJlbmFtZShwYXJ0aWNpcGFudCA9IFBhcnRpY2lwYW50TmFtZSwKIyAgICAgICAgICBncm91cCA9ICJbR3JvdXBdVmFsdWUiLAojICAgICAgICAgIGxhbmd1YWdlID0gIltMYW5ndWFnZV1WYWx1ZSIsCiMgICAgICAgICAgbWVkaWEgPSBNZWRpYU5hbWUsCiMgICAgICAgICAgeCA9ICJHYXplUG9pbnRYIChNQ1NweCkiLAojICAgICAgICAgIHkgPSAiR2F6ZVBvaW50WSAoTUNTcHgpIiwKIyAgICAgICAgICBnYXplaW5kZXggPSBHYXplUG9pbnRJbmRleCkgJT4lCiMgICBzZWxlY3QocGFydGljaXBhbnQsZ3JvdXAsbGFuZ3VhZ2UsbWVkaWEseCx5LGdhemVpbmRleCkgJT4lCiMgICBhZGRfY29sdW1uKHN0b3J5PU5BLGRpcmVjdGlvbj1OQSxtYWluZ3JvdXA9TkEpICU+JQojICAgbXV0YXRlKHN0b3J5ID0gY2FzZV93aGVuKAojICAgICBzdHJfZGV0ZWN0KG1lZGlhLCJiZWFycyIpIH4gImJlYXJzIiwKIyAgICAgc3RyX2RldGVjdChtZWRpYSwiY2luZGVyZWxsYSIpIH4gImNpbmRlcmVsbGEiLCAgIAojICAgICBzdHJfZGV0ZWN0KG1lZGlhLCJtaWRhcyIpIH4gIm1pZGFzIiwKIyAgICAgc3RyX2RldGVjdChtZWRpYSwicmVkcmlkaW5naG9vZCIpIH4gInJlZHJpZGluZ2hvb2QiKSkgJT4lCiMgICBtdXRhdGUoZGlyZWN0aW9uID0gY2FzZV93aGVuKAojICAgICBzdHJfZGV0ZWN0KG1lZGlhLCJGVyIpIH4gImZvcndhcmQiLAojICAgICBzdHJfZGV0ZWN0KG1lZGlhLCJFUiIpIH4gInJldmVyc2VkIikpICU+JQojICAgbXV0YXRlKG1lZGlhID0gc3RyX2Moc3RvcnksZGlyZWN0aW9uLCBzZXA9Il8iKSkgJT4lCiMgICBtdXRhdGUobGFuZ3VhZ2UgPSBjYXNlX3doZW4oCiMgICAgIHN0cl9kZXRlY3QobGFuZ3VhZ2UsIkVhcmx5QVNMIikgfiAiRWFybHkiLAojICAgICBzdHJfZGV0ZWN0KGxhbmd1YWdlLCJMYXRlQVNMIikgfiAiTGF0ZSIsCiMgICAgIHN0cl9kZXRlY3QobGFuZ3VhZ2UsIk5vdmljZUFTTF9UcmFpbmVkIikgfiAiVHJhaW5lZCIsCiMgICAgIHN0cl9kZXRlY3QobGFuZ3VhZ2UsIk5vdmljZUFTTCIpIH4gIk5vdmljZSIpKSAlPiUKIyAgIG11dGF0ZShtYWluZ3JvdXAgPSBzdHJfYyhncm91cCxsYW5ndWFnZSwgc2VwPSIiKSkgJT4lCiMgICBmaWx0ZXIoIWlzLm5hKG1haW5ncm91cCkpCiMgZ2xpbXBzZShyYXdkYXRhKQpgYGAKCk5vdyB0aGF0IGl0J3MgaW4gdGhlIHJpZ2h0IGZvcm1hdC4uLml0J3MgZWFzeSB0byBnZXQgd2hhdCB3ZSBuZWVkISA6LUQgCgojIEFuYWx5c2lzCgpGaXJzdCwgbGV0J3MgdHJpbSBlYWNoIHBhcnRpY2lwYW50J3MgZGF0YSwgZ2V0dGluZyByaWQgb2YgdGhlIGZpcnN0IDMwIHNhbXBsZXMgKDAuNSBzZWNzKS4gVGhlbiB3ZSdsbCBnZXQgdGhlIHRoZSBtZWFuIHggYW5kIHkgY29vcmRpbmF0ZSBmb3IgZWFjaCBzdG9yeSBmb3IgZWFjaCBwYXJ0aWNpcGFudC4KCmBgYHtyfQoKI3dyaXRlX2NzdihyYXdkYXRhLCJhZHVsdHZpZXdpbmdzcGFjZXJhdy5jc3YiKQpyYXdkYXRhIDwtIHJlYWRfY3N2KCIuLi9BZHVsdCBEYXRhL3Jhd2RhdGEvYWR1bHR2aWV3aW5nc3BhY2VyYXcuY3N2IikKCnJhd2RhdGEgPC0gcmF3ZGF0YSAlPiUKICBhcnJhbmdlKHBhcnRpY2lwYW50LG1lZGlhLGdhemVpbmRleCkgJT4lCiAgZ3JvdXBfYnkocGFydGljaXBhbnQsbWVkaWEpICU+JQogIHNsaWNlKDMwOm4oKSkKCm1lYW5zIDwtIHJhd2RhdGEgJT4lCiAgZ3JvdXBfYnkocGFydGljaXBhbnQsbWVkaWEpICU+JQogIHN1bW1hcmlzZSh4ID0gbWVhbih4LG5hLnJtPVRSVUUpLAogICAgICAgICAgICB5ID0gbWVhbih5LG5hLnJtPVRSVUUpKQpoZWFkKG1lYW5zLDEwKQpgYGAKCkFuZCBJIGNhbiBnZXQgeCBvciB5IHBsb3RzIG9mIG9uZSBwYXJ0aWNpcGFudCBhY3Jvc3MgNCBzdG9yaWVzLiBMZXQncyBkbyBBZGFtIChtZT8pLiBXZSdsbCBzZXQgdGhlIHggYW5kIHkgbGltaXRzIHRvIHRoZSB3aG9sZSB3aWR0aCBvZiB0aGUgVG9iaWkgbW9uaXRvciAoMTYwMHgxMjAwKS4gQnV0IGJlY2F1c2UgVG9iaWkgY29uc2lkZXJzICgwLDApIHRvIGJlIHRoZSB1cHBlciBsZWZ0IGNvcm5lciAoYW5kIG5vdCB0aGUgYm90dG9tIGxlZnQgY29ybmVyKSwgd2UgYWxzbyBuZWVkIHRvIGZsaXAgdGhlIHkgYXhpcy4gCgpgYGB7cn0KYWRhbSA8LSBmaWx0ZXIocmF3ZGF0YSxwYXJ0aWNpcGFudD09IkFkYW0iKSAlPiUgbXV0YXRlKHkgPSB5Ki0xKQpnZ3Bsb3QoYWRhbSxhZXMoeD14LHk9eSxjb2xvcj1tZWRpYSkpICsgZ2VvbV9wb2ludChzaXplPTAuMSkgKyBnZW9tX3BhdGgoKSArIGZhY2V0X3dyYXAoIm1lZGlhIixuY29sPTIsbnJvdz0yKSArIGd1aWRlcyhjb2xvcj0ibm9uZSIpICsgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0PWMoMCwxNjAwKSkgKyBzY2FsZV95X2NvbnRpbnVvdXMobGltaXQ9YygtMTIwMCwwKSkKYGBgCkNvb2wsIHllYWg/IAoKTGV0J3MgdHJ5IHRoaXMgYWdhaW4gYnV0IGxldCB0aGUgeCBhbmQgeSBsaW1pdHMgbWF0Y2ggdGhlIGRhdGEuIFRoYXQgd2lsbCAiem9vbSIgaW4uIFdlJ2xsIGFsc28gZ2V0IHJpZCBvZiB0aGF0IHdlaXJkIHJpZ2h0LXNpZGUgb3V0bGllciBpbiBSUkguICBOZWF0b29vb28uIAoKYGBge3J9CmFkYW0gPC0gZmlsdGVyKGFkYW0seDw4MDApCmdncGxvdChhZGFtLGFlcyh4PXgseT15LGNvbG9yPW1lZGlhKSkgKyBnZW9tX3BvaW50KHNpemU9MC4xKSArIGdlb21fcGF0aCgpICsgZmFjZXRfd3JhcCgibWVkaWEiLG5jb2w9Mixucm93PTIpICsgZ3VpZGVzKGNvbG9yPSJub25lIikgCmBgYAoKIyBJUVIgCk5vdyBsZXQncyBnZXQgdGhlIG1pZGRsZSA1MCUgKGFrYSB0aGUgSVFSKSBvZiB4IGFuZCB5IGZvciBlYWNoIHBhcnRpY2lwYW50J3Mgc3RvcnkgKHdlJ3ZlIGFscmVhZHkgdHJpbW1lZCB0aGUgZmlyc3QgMzAgc2FtcGxlcykuIFRoYXQgc2hvdWxkIGFsc28gdGFrZSBjYXJlIG9mIGZ1cnRoZXIgd2VpcmQgb3V0bGllcnMuIEFuZCB3ZSBhcmUgZGVmaW5pbmcgInZpZXdpbmcgc3BhY2UiIGFzIHRoZSBJUVIgb2YgdGhlIHggYW5kIHkgYXhpcy4gCgpgYGB7cn0KaXFyIDwtIHJhd2RhdGEgJT4lCiAgZ3JvdXBfYnkocGFydGljaXBhbnQsbWVkaWEpICU+JQogIGRwbHlyOjpzdW1tYXJpemUoeElRUiA9IElRUih4LG5hLnJtPVRSVUUpLAogICAgICAgICAgICAgICAgICAgeUlRUiA9IElRUih5LG5hLnJtPVRSVUUpLAogICAgICAgICAgICAgICAgICAgeG1lZCA9IG1lZGlhbih4LCBuYS5ybT1UUlVFKSwKICAgICAgICAgICAgICAgICAgIHltZWQgPSBtZWRpYW4oeSwgbmEucm09VFJVRSkpCmhlYWQoaXFyLDEwKQpgYGAKCkFuZCBjaGVjayBvdXQgdGhlIGhpc3RvZ3JhbXM6CgpgYGB7cn0KaXFyICU+JSAKICBnYXRoZXIoYXhpcyxpcXIseElRUjp5SVFSKSAlPiUKICBnZ3Bsb3QoYWVzKHg9aXFyLGZpbGw9YXhpcykpICsgZ2VvbV9oaXN0b2dyYW0oKSArIGZhY2V0X2dyaWQoYXhpc34uKQpgYGAKClRoZXJlJ3Mgb25lIHdlaXJkIG91dGxpZXIgZm9yIHhJUVIuIFRoYXQncyBSZWJlY2NhLiBMZXQncyBsb29rIGF0IGhlciBkYXRhLiAKCmBgYHtyfQpyZWJlY2NhIDwtIGZpbHRlcihyYXdkYXRhLHBhcnRpY2lwYW50PT0iUmViZWNjYSIpICU+JSBtdXRhdGUoeSA9IHkqLTEpCmdncGxvdChyZWJlY2NhLGFlcyh4PXgseT15LGNvbG9yPW1lZGlhKSkgKyBnZW9tX3BvaW50KHNpemU9MC4xKSArIGdlb21fcGF0aCgpICsgZmFjZXRfd3JhcCgibWVkaWEiLG5jb2w9Mixucm93PTIpICsgZ3VpZGVzKGNvbG9yPSJub25lIikgKyB4bGltKDAsMTQ0MCkgKyB5bGltKC0xMDgwLDApCmBgYAoKU28gd2UnbGwgcmVtb3ZlIFJlYmVjY2EncyBtaWRhc19mb3J3YXJkIHN0b3J5LiBOZXh0LCBjaGVjayB0aGUgbWVkaWFucy4KCmBgYHtyfQppcXIgJT4lIAogIGdhdGhlcihheGlzLG1lZCx4bWVkOnltZWQpICU+JQogIGdncGxvdChhZXMoeD1tZWQsZmlsbD1heGlzKSkgKyBnZW9tX2hpc3RvZ3JhbSgpICsgZmFjZXRfZ3JpZChheGlzfi4pCgpgYGAKCkxvb2tzIGdyZWF0ISBCdXQgaXQgYWxzbyBsb29rcyBwcmV0dHkgaW50ZXJlc3RpbmcgZG9lc24ndCBpdC4uLlkgbWVkaWFuIGhhcyBhIHdpZGVyIHNwcmVhZCB0aGFuIFggbWVkaWFuLiBUaGF0IHdvdWxkIG1ha2Ugc2Vuc2UuIAoKTm93IHdlJ3JlIHJlYWR5IHRvIGRvIHN0YXRzIGJhc2VkIG9uIGdyb3VwLCBkaXJlY3Rpb24sIGV0Yy4gCgpgYGB7cn0KcmJjIDwtIHRyaWJibGUofnBhcnRpY2lwYW50LCB+bWVkaWEsICJSZWJlY2NhIiwibWlkYXNfZm9yd2FyZCIpCmlxciA8LSBpcXIgJT4lCiAgdW5ncm91cCgpICU+JQogIGFudGlfam9pbihyYmMsIGJ5PWMoInBhcnRpY2lwYW50IiwibWVkaWEiKSkgIyBmb3Igc29tZSByZWFzb24gZmlsdGVyIHdvdWxkbid0IHdvcmsKCnN1YmplY3RpbmZvIDwtIHJhd2RhdGEgJT4lCiAgc2VsZWN0KHBhcnRpY2lwYW50LG1haW5ncm91cCxncm91cCxsYW5ndWFnZSxtZWRpYSxzdG9yeSxkaXJlY3Rpb24pICU+JQogIGRpc3RpbmN0KCkgJT4lCiAgZmlsdGVyKCFpcy5uYShwYXJ0aWNpcGFudCkpCgppcXIgPC0gaXFyICU+JQogIGxlZnRfam9pbihzdWJqZWN0aW5mbyxieT1jKCJwYXJ0aWNpcGFudCIsIm1lZGlhIikpICU+JQogIGZpbHRlcighaXMubmEobWFpbmdyb3VwKSkKCmlxci5nYXRoZXIgPC0gaXFyICU+JSBnYXRoZXIoYXhpcyx2YWx1ZSx4SVFSOnltZWQpCmlxci5pcXIgPC0gZmlsdGVyKGlxci5nYXRoZXIsYXhpcz09InhJUVIiIHwgYXhpcz09InlJUVIiKQppcXIubWVkIDwtIGZpbHRlcihpcXIuZ2F0aGVyLGF4aXM9PSJ4bWVkIiB8IGF4aXM9PSJ5bWVkIikKCgpnZ3Bsb3QoaXFyLmlxcixhZXMoeD1tYWluZ3JvdXAseT12YWx1ZSxmaWxsPWRpcmVjdGlvbikpICsgCiAgZ2VvbV9ib3hwbG90KCkgKyB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsaGp1c3Q9MSkpICsKICBmYWNldF9ncmlkKC5+YXhpcykKYGBgCgpBbmQgdGhlIG1lZGlhbiB4IGFuZCB5IHBvc2l0aW9uICh0aGlzIGFzc3VtZXMgYWxsIGNhbGlicmF0aW9ucyBhcmUgY29ycmVjdCk6CgpgYGB7cn0KZ2dwbG90KGlxci5tZWQsYWVzKHg9bWFpbmdyb3VwLHk9dmFsdWUsZmlsbD1kaXJlY3Rpb24pKSArIAogIGdlb21fYm94cGxvdCgpICsgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LGhqdXN0PTEpKSArCiAgZmFjZXRfZ3JpZCgufmF4aXMpCmBgYAoKCkZpcnN0LCBkb2VzIHJldmVyc2FsIGhhdmUgYW4gZWZmZWN0IG9uIFggSVFSPyBXZSBoYXZlIHJhbmRvbSBpbnRlcmNlcHRzIGZvciBlYWNoIHBhcnRpY2lwYW50IGFuZCBtZWRpYSwgYW5kIGEgcmFuZG9tIHNsb3BlIGFkanVzdG1lbnQgZm9yIHJldmVyc2VkIGZvciBlYWNoIHBhcnRpY2lwYW50LiAKCmBgYHtyfQp4aXFyLnJldmVyc2FsIDwtIGxtZXIoeElRUiB+IGRpcmVjdGlvbiArIChkaXJlY3Rpb258cGFydGljaXBhbnQpICsgKDF8bWVkaWEpLCBkYXRhID0gaXFyKQpzdW1tYXJ5KHhpcXIucmV2ZXJzYWwpJGNvZWZmaWNpZW50cwpgYGAKIApXZWxwLiBOby4gWSBJUVI/CmBgYHtyfQp5aXFyLnJldmVyc2FsIDwtIGxtZXIoeUlRUiB+IGRpcmVjdGlvbiArIChkaXJlY3Rpb258cGFydGljaXBhbnQpICsgKDF8bWVkaWEpLCBkYXRhID0gaXFyKQpzdW1tYXJ5KHlpcXIucmV2ZXJzYWwpJGNvZWZmaWNpZW50cwpgYGAKCk5vIGVmZmVjdCBoZXJlIGVpdGhlci4gTGV0J3MgdHJ5IGFkZGluZyBtYWluZ3JvdXBzLiBYIElRUiBmaXJzdC4gCmBgYHtyfQp4aXFyLmdyb3VwIDwtIGxtZXIoeElRUiB+IGRpcmVjdGlvbiAqIG1haW5ncm91cCArIChkaXJlY3Rpb258cGFydGljaXBhbnQpICsgKDF8bWVkaWEpLCBkYXRhID0gaXFyKQpzdW1tYXJ5KHhpcXIuZ3JvdXApJGNvZWZmaWNpZW50cwpgYGAKClRoaXMgbWVhbnMgdGhlcmUgaXMgYSBzaWduaWZpY2FudCBkaWZmZXJlbmNlIG9mIERlYWZFYXJseSB2cy4gSGVhcmluZ0xhdGUgYW5kIEhlYXJpbmdUcmFpbmVkIG9uIHhJUVIsIGJ1dCBpbiB0aGUgZm9yd2FyZCBjb25kaXRpb24gb25seS4gQmFzaWNhbGx5LCB0aG9zZSB0d28gZ3JvdXBzIGhhdmUgYSAid2lkZXIiIHZpZXdpbmcgc3BhY2UgdGhhbiBvdGhlciBncm91cHMuCgpgYGB7cn0KeWlxci5ncm91cCA8LSBsbWVyKHlJUVIgfiBkaXJlY3Rpb24gKiBtYWluZ3JvdXAgKyAoZGlyZWN0aW9ufHBhcnRpY2lwYW50KSArICgxfG1lZGlhKSwgZGF0YSA9IGlxcikKc3VtbWFyeSh5aXFyLmdyb3VwKSRjb2VmZmljaWVudHMKYGBgCk5vIGRpZmZlcmVuY2VzIGFtb25nIGdyb3VwcyBvciByZXZlcnNhbCBlZmZlY3QgZm9yIHhJUVIuIFZpZXdpbmcgc3BhY2UgZG9lc24ndCBnZXQgc2lnbmlmaWNhbnRseSB0YWxsZXIgb3Igc2hvcnRlci4gCgojIFZpZXdpbmcgU3BhY2UgQ2hhcnRzCkkgd2FudCB0byBsZWFybiBob3cgdG8gbWFrZSByZWN0YW5nbGUgcGxvdHMgc28gaGVyZSB3ZSBnby4gVXNpbmcgZWFjaCBwYXJ0aWNpcGFudCdzIGZvdXIgeCBhbmQgeSBtZWRpYW5zIGFuZCA0IHggYW5kIHkgSVFScyAob25lIHNldCBmb3IgZWFjaCBzdG9yeSwgZm9yIDQgc3RvcmllcykuIFNvIEkgY2FuIGdldCB0aGUgbG9naWMgYW5kIGNvZGUgZG93bi4gTGV0J3MgYXNzdW1lIGFsbCBjYWxpYnJhdGlvbnMgd2VyZSBjb3JyZWN0LiBIZXJlJ3MgdGhlIGNoYXJ0IGZvciB0aGUgd2hvbGUgbWVkaWEgc2l6ZSBvZiAxNDQweDEwODAgKGFzIHJlcG9ydGVkIGluIFRvYmlpKS4gCmBgYHtyfQojIEluIHRoaXMgb3JkZXIsIHdlJ2xsIGdldCBhIGdyYW5kIG1lZGlhbiBieSB0YWtpbmcgYSBwYXJ0aWNpcGFudCdzIG1lZGlhbiBhY3Jvc3MgdGhlaXIgNCBzdG9yaWVzLCB0aGFuIHRoZSBtZWRpYW4gZm9yIGZvcndhcmQgYW5kIHJldmVyc2UgYWNyb3NzIGFsbCBwYXJ0aWNpcGFudHMuIAptZWRpYW5zIDwtIGlxciAlPiUKICBncm91cF9ieShtYWluZ3JvdXAsIHBhcnRpY2lwYW50LGRpcmVjdGlvbikgJT4lCiAgZHBseXI6OnN1bW1hcml6ZSh4SVFSID0gbWVkaWFuKHhJUVIsbmEucm09VFJVRSksCiAgICAgICAgICAgICAgICAgICB5SVFSID0gbWVkaWFuKHlJUVIsbmEucm09VFJVRSksCiAgICAgICAgICAgICAgICAgICB4bWVkID0gbWVkaWFuKHhtZWQsbmEucm09VFJVRSksCiAgICAgICAgICAgICAgICAgICB5bWVkID0gbWVkaWFuKHltZWQsbmEucm09VFJVRSkpICU+JQogIGdyb3VwX2J5KG1haW5ncm91cCwgZGlyZWN0aW9uKSAlPiUgCiAgZHBseXI6OnN1bW1hcml6ZSh4SVFSID0gbWVkaWFuKHhJUVIsbmEucm09VFJVRSksCiAgICAgICAgICAgICAgICAgICB5SVFSID0gbWVkaWFuKHlJUVIsbmEucm09VFJVRSksCiAgICAgICAgICAgICAgICAgICB4ID0gbWVkaWFuKHhtZWQsbmEucm09VFJVRSksCiAgICAgICAgICAgICAgICAgICB5ID0gbWVkaWFuKHltZWQsbmEucm09VFJVRSkpCgptZWRpYW5zIDwtIG1lZGlhbnMgJT4lCiAgbXV0YXRlKHkgPSB5Ki0xLAogICAgICAgICB4bWluID0geC0oeElRUi8yKSwKICAgICAgICAgeG1heCA9IHgrKHhJUVIvMiksCiAgICAgICAgIHltaW4gPSB5LSh5SVFSLzIpLAogICAgICAgICB5bWF4ID0geSsoeUlRUi8yKSkKCmltZyA8LSByZWFkUE5HKCJjaW5keS5wbmciKQpnIDwtIHJhc3Rlckdyb2IoaW1nLCBpbnRlcnBvbGF0ZT1UUlVFLCB3aWR0aD11bml0KDEsIm5wYyIpLCBoZWlnaHQ9dW5pdCgxLCJucGMiKSkgCgpnZ3Bsb3QobWVkaWFucywgYWVzKGZpbGw9ZGlyZWN0aW9uLGNvbG9yPWRpcmVjdGlvbikpICsKICBhbm5vdGF0aW9uX2N1c3RvbShnLCB4bWluPS1JbmYsIHhtYXg9SW5mLCB5bWluPS1JbmYsIHltYXg9SW5mKSArCiAgZ2VvbV9yZWN0KGFlcyh4bWluPXhtaW4seW1pbj15bWluLHhtYXg9eG1heCx5bWF4PXltYXgpLGFscGhhPS4xKSArIAogIHRoZW1lX2xpbmVkcmF3KCkgKyAKICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygwLDE0NDApLCBleHBhbmQgPSBjKDAsIDApKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoLTEwODAsMCksIGV4cGFuZCA9IGMoMCwgMCkpICsKICBmYWNldF93cmFwKCJtYWluZ3JvdXAiKQoKIyBnZ3Bsb3QoaXFyLmdsb2JhbCwgYWVzKGZpbGw9ZGlyZWN0aW9uLGNvbG9yPWRpcmVjdGlvbikpICsKIyAgIGFubm90YXRpb25fY3VzdG9tKGcsIHhtaW49LUluZiwgeG1heD1JbmYsIHltaW49LUluZiwgeW1heD1JbmYpICsKIyAgIGdlb21fcmVjdChhZXMoeG1pbj14bWluLHltaW49eW1pbix4bWF4PXhtYXgseW1heD15bWF4KSxhbHBoYT0uMSkgKyAKIyAgIHRoZW1lX21pbmltYWwoKSArIHhsaW0oMCwxNDQwKSArIHlsaW0oLTEwODAsMCkgKwojICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PS0xMDgwKzg4NSkgKwojICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PS0xMDgwKzUyNSkgKyAKIyAgIGFubm90YXRlKGdlb209InRleHQiLCB4ID0gMzAwLCB5ID0gLTEwODArNTU1LCBsYWJlbCA9ICJ1cHBlciBzaG91bGRlciBwb2ludCIpICsKIyAgIGFubm90YXRlKGdlb209InBvaW50IiwgeCA9IDUzNSwgeSA9IC0xMDgwKzUyNSkgKyAKIyAgIGFubm90YXRlKGdlb209InRleHQiLCB4ID0gNTM1LCB5ID0gLTEwODArOTEwLCBsYWJlbCA9ICJoZWlnaHQgbGluZSIpICsgCiMgICBhbm5vdGF0ZShnZW9tPSJyZWN0IiwgeG1pbiA9IDUzNSwgeG1heCA9IDUzNSszNjUsIHltaW4gPSAtNTI1LTU1MSwgeW1heCA9IC0xMDgwKzUyNSwgZmlsbD0ibWFyb29uIiwgY29sb3I9ImJsYWNrIiwgYWxwaGE9MC41KSArIAojICAgYW5ub3RhdGUoZ2VvbT0idGV4dCIsIHggPSA3MDAsIHkgPSAtOTAwLCBsYWJlbCA9ICJ0b3JzbyIpCmBgYAoKWWF5eXkhIEl0IHdvcmtlZCEgQSBiaXQgaGFyZCB0byBzZWUhIExldCdzIHpvb20gaW4uIAoKYGBge3J9CiMgZ2dwbG90KGlxci5nbG9iYWwsIGFlcyhmaWxsPWRpcmVjdGlvbixjb2xvcj1kaXJlY3Rpb24pKSArCiMgICBhbm5vdGF0aW9uX2N1c3RvbShnLCB4bWluPTAsIHhtYXg9MTQ0MCwgeW1pbj0tMTA4MCwgeW1heD0wKSArCiMgICBnZW9tX3JlY3QoYWVzKHhtaW49eG1pbix5bWluPXltaW4seG1heD14bWF4LHltYXg9eW1heCksYWxwaGE9LjEpICsgCiMgICB0aGVtZV9taW5pbWFsKCkgKyB4bGltKDYwMCw4MDApICsgeWxpbSgtNTAwLC0zMDApCgpnZ3Bsb3QobWVkaWFucywgYWVzKGZpbGw9ZGlyZWN0aW9uLGNvbG9yPWRpcmVjdGlvbikpICsKICBnZW9tX3JlY3QoYWVzKHhtaW49eG1pbix5bWluPXltaW4seG1heD14bWF4LHltYXg9eW1heCksYWxwaGE9LjEpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgeGxpbSg2NzcsNzQzKSArIHlsaW0oLTQyMSwtMzcwKQoKI2dncGxvdChpcXIuZ2xvYmFsLCBhZXMoZmlsbD1kaXJlY3Rpb24sY29sb3I9ZGlyZWN0aW9uKSkgKwojICBnZW9tX3JlY3QoYWVzKHhtaW49eG1pbix5bWluPXltaW4seG1heD14bWF4LHltYXg9eW1heCksYWxwaGE9LjEpCmBgYApJIGhhdmUgdG8gZmlndXJlIG91dCBob3cgdG8gZ2V0IENpbmR5IHRvIHpvb20gaW4gLSBvciBjcm9wIGEgem9vbWVkIGluIHZlcnNpb24gb2YgQ2luZHkgbWFudWFsbHkgYW5kIHVzZSB0aGF0LgoKIyBWaWV3aW5nIFNwYWNlIENoYXJ0cyBmb3IgSW5kaXZpZHVhbHMKTm93IGxldCdzIHNlZSB0aGUgdmFyaWF0aW9uIGluIHZpZXdpbmcgc3BhY2VzIGZvciBhbGwgb3VyIGluZGl2aWR1YWxzLiBTaG91bGQgYmUgZnVuLgoKYGBge3IgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTI2fQppcXIuaW5kaXZpZHVhbHMgPC0gaXFyICU+JQogIHJlbmFtZSh4ID0geG1lZCwKICAgICAgICAgeSA9IHltZWQpICU+JQogIG11dGF0ZSh5ID0geSotMSwKICAgICAgICAgeG1pbiA9IHgtKHhJUVIvMiksCiAgICAgICAgIHhtYXggPSB4Kyh4SVFSLzIpLAogICAgICAgICB5bWluID0geS0oeUlRUi8yKSwKICAgICAgICAgeW1heCA9IHkrKHlJUVIvMikpCgpnZ3Bsb3QoaXFyLmluZGl2aWR1YWxzLCBhZXMoZmlsbD1kaXJlY3Rpb24sY29sb3I9ZGlyZWN0aW9uKSkgKwogIGFubm90YXRpb25fY3VzdG9tKGcsIHhtaW49LUluZiwgeG1heD1JbmYsIHltaW49LUluZiwgeW1heD1JbmYpICsKICBnZW9tX3JlY3QoYWVzKHhtaW49eG1pbix5bWluPXltaW4seG1heD14bWF4LHltYXg9eW1heCksYWxwaGE9LjEpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgeGxpbSgwLDE0NDApICsgeWxpbSgtMTA4MCwwKSArIGZhY2V0X3dyYXAoImRpcmVjdGlvbiIpICsKICBnZ3RpdGxlKCJ3aXRoIElRUnMiKQpgYGAKCiMgU3RhbmRhcmQgRGV2aWF0aW9ucz8KCkxldCdzIHRyeSB1c2luZyBzdGFuZGFyZCBkZXZpYXRpb25zIGluc3RlYWQuCgpgYGB7ciBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MjZ9CnNkIDwtIHJhd2RhdGEgJT4lCiAgZ3JvdXBfYnkocGFydGljaXBhbnQsbWVkaWEpICU+JQogIGRwbHlyOjpzdW1tYXJpemUoeHNkID0gc2QoeCxuYS5ybT1UUlVFKSwKICAgICAgICAgICAgICAgICAgIHlzZCA9IHNkKHksbmEucm09VFJVRSksCiAgICAgICAgICAgICAgICAgICB4bWVhbiA9IG1lYW4oeCwgbmEucm09VFJVRSksCiAgICAgICAgICAgICAgICAgICB5bWVhbiA9IG1lYW4oeSwgbmEucm09VFJVRSkpCgpzZC5pbmRpdmlkdWFscyA8LSBzZCAlPiUKICByZW5hbWUoeCA9IHhtZWFuLAogICAgICAgICB5ID0geW1lYW4pICU+JQogIG11dGF0ZSh5ID0geSotMSwKICAgICAgICAgeG1pbiA9IHgteHNkLAogICAgICAgICB4bWF4ID0geCt4c2QsCiAgICAgICAgIHltaW4gPSB5LXlzZCwKICAgICAgICAgeW1heCA9IHkreXNkKSAlPiUKICBsZWZ0X2pvaW4oc3ViamVjdGluZm8sYnk9YygicGFydGljaXBhbnQiLCJtZWRpYSIpKSAlPiUKICBmaWx0ZXIoIWlzLm5hKG1haW5ncm91cCkpCgoKZ2dwbG90KHNkLmluZGl2aWR1YWxzLCBhZXMoZmlsbD1kaXJlY3Rpb24sY29sb3I9ZGlyZWN0aW9uKSkgKwogIGFubm90YXRpb25fY3VzdG9tKGcsIHhtaW49LUluZiwgeG1heD1JbmYsIHltaW49LUluZiwgeW1heD1JbmYpICsKICBnZW9tX3JlY3QoYWVzKHhtaW49eG1pbix5bWluPXltaW4seG1heD14bWF4LHltYXg9eW1heCksYWxwaGE9LjEpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgeGxpbSgwLDE0NDApICsgeWxpbSgtMTA4MCwwKSArIGZhY2V0X3dyYXAoImRpcmVjdGlvbiIpICsKICBnZ3RpdGxlKCJ3aXRoIFNEcyIpCgoKYGBgCgpOb3cgbGV0J3MgbWFrZSBPdXRlciBMaW1pdHMgY2hhcnRzIHdoaWNoIGlzIElRUiArLy0gMiBTRHMuIApgYGB7ciBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MjZ9CnNkLmluZGl2aWR1YWxzIDwtIHNlbGVjdChzZC5pbmRpdmlkdWFscyxwYXJ0aWNpcGFudCxtZWRpYSx4c2QseXNkKQppcXJzZC5pbmRpdmlkdWFscyA8LSBsZWZ0X2pvaW4oaXFyLmluZGl2aWR1YWxzLHNkLmluZGl2aWR1YWxzLGJ5PWMoInBhcnRpY2lwYW50IiwibWVkaWEiKSkgJT4lCiAgbXV0YXRlKHhtaW4gPSB4bWluLSgyKnhzZCksCiAgICAgICAgIHhtYXggPSB4bWF4KygyKnhzZCksCiAgICAgICAgIHltaW4gPSB5bWluLSgyKnlzZCksCiAgICAgICAgIHltYXggPSB5bWF4KygyKnlzZCkpCgpnZ3Bsb3QoaXFyc2QuaW5kaXZpZHVhbHMsIGFlcyhmaWxsPWRpcmVjdGlvbixjb2xvcj1kaXJlY3Rpb24pKSArCiAgYW5ub3RhdGlvbl9jdXN0b20oZywgeG1pbj0tSW5mLCB4bWF4PUluZiwgeW1pbj0tSW5mLCB5bWF4PUluZikgKwogIGdlb21fcmVjdChhZXMoeG1pbj14bWluLHltaW49eW1pbix4bWF4PXhtYXgseW1heD15bWF4KSxhbHBoYT0uMSkgKyAKICB0aGVtZV9taW5pbWFsKCkgKyB4bGltKDAsMTQ0MCkgKyB5bGltKC0xMDgwLDApICsgZmFjZXRfd3JhcCgiZGlyZWN0aW9uIikgKwogIGdndGl0bGUoIndpdGggU0RzIikKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MjZ9CmlxcnNkLmluZGl2aWR1YWxzIDwtIGlxcnNkLmluZGl2aWR1YWxzICU+JQogIGdyb3VwX2J5KGRpcmVjdGlvbikgJT4lCiAgZHBseXI6OnN1bW1hcml6ZSh4ID0gbWVhbih4LG5hLnJtPVRSVUUpLAogICAgICAgICAgICB5ID0gbWVhbih5LG5hLnJtPVRSVUUpLAogICAgICAgICAgICB4bWluID0gbWVhbih4bWluLG5hLnJtPVRSVUUpLAogICAgICAgICAgICB5bWluID0gbWVhbih5bWluLG5hLnJtPVRSVUUpLAogICAgICAgICAgICB4bWF4ID0gbWVhbih4bWF4LG5hLnJtPVRSVUUpLAogICAgICAgICAgICB5bWF4ID0gbWVhbih5bWF4LG5hLnJtPVRSVUUpKQpnZ3Bsb3QoaXFyc2QuaW5kaXZpZHVhbHMsIGFlcyhmaWxsPWRpcmVjdGlvbixjb2xvcj1kaXJlY3Rpb24pKSArCiAgYW5ub3RhdGlvbl9jdXN0b20oZywgeG1pbj0tSW5mLCB4bWF4PUluZiwgeW1pbj0tSW5mLCB5bWF4PUluZikgKwogIGdlb21fcmVjdChhZXMoeG1pbj14bWluLHltaW49eW1pbix4bWF4PXhtYXgseW1heD15bWF4KSxhbHBoYT0uMSkgKyAKICB0aGVtZV9taW5pbWFsKCkgKyB4bGltKDAsMTQ0MCkgKyB5bGltKC0xMDgwLDApICsgZmFjZXRfd3JhcCgiZGlyZWN0aW9uIikgKwogIGdndGl0bGUoIkF2ZXJhZ2Ugb2YgYWJvdmUgY2hhcnQgKHJhaW4ncyBvdXRlciBsaW1pdHMpIikKCmBgYAoKIyBYWSBTcGFjZSBEYXRhIC0gTXVsdGlwbGUgUGxvdHMKCkZpcnN0IGxldCdzIHByZXAgdGhlIGRhdGEuIApgYGB7cn0KbXVsdGlwbGVzIDwtIHJhd2RhdGEgJT4lCiAgZmlsdGVyKCFpcy5uYSh4KSkgJT4lCiAgZmlsdGVyKCFpcy5uYSh5KSkgJT4lCiAgcmVuYW1lKGxhbmcgPSBtYWluZ3JvdXAsCiAgICAgICAgIG5hbWUgPSBwYXJ0aWNpcGFudCkgJT4lCiAgZ3JvdXBfYnkobmFtZSwgbGFuZywgZGlyZWN0aW9uLCBzdG9yeSkgJT4lCiAgc3VtbWFyaXNlKHhJUVIgPSBJUVIoeCxuYS5ybT1UUlVFKSwKICAgICAgICAgICAgeUlRUiA9IElRUih5LG5hLnJtPVRSVUUpLAogICAgICAgICAgICB4bWVkID0gbWVkaWFuKHgsIG5hLnJtPVRSVUUpLAogICAgICAgICAgICB5bWVkID0gbWVkaWFuKHksIG5hLnJtPVRSVUUpLAogICAgICAgICAgICBhcmVhID0geElRUip5SVFSLAogICAgICAgICAgICB4XzkwID0gcXVhbnRpbGUoeCwgLjk1LCBuYS5ybT1UUlVFKSAtIHF1YW50aWxlKHgsIC4wNSwgbmEucm09VFJVRSksCiAgICAgICAgICAgIHlfOTAgPSBxdWFudGlsZSh5LCAuOTUsIG5hLnJtPVRSVUUpIC0gcXVhbnRpbGUoeSwgLjA1LCBuYS5ybT1UUlVFKSwKICAgICAgICAgICAgYXJlYV85MCA9ICh4XzkwKSAqICh5XzkwKSwKICAgICAgICAgICAgeF9tZWFuID0gbWVhbih4LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICB5X21lYW4gPSBtZWFuKHksIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIHhfc2QgPSBzZCh4LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICB5X3NkID0gc2QoeSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgeF8xc2QgPSAoeF9tZWFuK3hfc2QpIC0gKHhfbWVhbi14X3NkKSwKICAgICAgICAgICAgeV8xc2QgPSAoeV9tZWFuK3lfc2QpIC0gKHlfbWVhbi15X3NkKSwKICAgICAgICAgICAgYXJlYV8xc2QgPSB4XzFzZCAqIHlfMXNkLAogICAgICAgICAgICB4XzJzZCA9ICh4X21lYW4rKHhfc2QqMikpIC0gKHhfbWVhbi0oeF9zZCoyKSksCiAgICAgICAgICAgIHlfMnNkID0gKHlfbWVhbisoeV9zZCoyKSkgLSAoeV9tZWFuLSh5X3NkKjIpKSwKICAgICAgICAgICAgYXJlYV8yc2QgPSB4XzJzZCAqIHlfMnNkKSAlPiUKICBncm91cF9ieShuYW1lLCBsYW5nLCBkaXJlY3Rpb24pICU+JQogIHN1bW1hcmlzZV9pZihpcy5kb3VibGUsIGZ1bnMobWVhbiksIG5hLnJtID0gVCkgJT4lCiAgZ3JvdXBfYnkobGFuZywgZGlyZWN0aW9uKSAlPiUKICBzdW1tYXJpc2VfaWYoaXMuZG91YmxlLCBmdW5zKG1lYW4pLCBuYS5ybSA9IFQpICU+JQogIGZpbHRlcihsYW5nID09ICJEZWFmRWFybHkiIHx8IGxhbmcgPT0gIkhlYXJpbmdOb3ZpY2UiKQoKaW1nIDwtIHBuZzo6cmVhZFBORygiY2luZHkucG5nIikKZyA8LSBncmlkOjpyYXN0ZXJHcm9iKGltZywgaW50ZXJwb2xhdGU9VFJVRSwgd2lkdGg9dW5pdCgxLCJucGMiKSwgaGVpZ2h0PXVuaXQoMSwibnBjIikpIAoKYGBgCgojIyBJUVIgKE1pZGRsZSA1MCUpCkxldCdzIHNlZS4gCmBgYHtyfQpjdXJyX2RhdGEgPC0gbXVsdGlwbGVzICU+JSAKICB1bmdyb3VwKCkgJT4lCiAgc2VsZWN0KGxhbmcsIGRpcmVjdGlvbiwgeG1lZCwgeW1lZCwgeElRUiwgeUlRUikgJT4lCiAgZ3JvdXBfYnkobGFuZywgZGlyZWN0aW9uKSAlPiUKICBzdW1tYXJpc2UoeG1pbiA9IHhtZWQtKHhJUVIvMiksCiAgICAgICAgIHhtYXggPSB4bWVkKyh4SVFSLzIpLAogICAgICAgICB5bWluID0gLTEqKHltZWQtKHlJUVIvMikpLAogICAgICAgICB5bWF4ID0gLTEqKHltZWQrKHlJUVIvMikpKQoKZ2dwbG90KGN1cnJfZGF0YSwgYWVzKGZpbGw9ZGlyZWN0aW9uLGNvbG9yPWRpcmVjdGlvbikpICsKICBhbm5vdGF0aW9uX2N1c3RvbShnLCB4bWluPS1JbmYsIHhtYXg9SW5mLCB5bWluPS1JbmYsIHltYXg9SW5mKSArCiAgZ2VvbV9yZWN0KGFlcyh4bWluPXhtaW4seW1pbj15bWluLHhtYXg9eG1heCx5bWF4PXltYXgpLGFscGhhPS4xKSArIAogIHRoZW1lX2xpbmVkcmF3KCkgKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsMTQ0MCksIGV4cGFuZCA9IGMoMCwgMCkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygtMTA4MCwwKSwgZXhwYW5kID0gYygwLCAwKSkgKwogIGZhY2V0X3dyYXAoImxhbmciKQpgYGAKCiMjIE1pZGRsZSA5MCUKU28gSSBjYWxjdWxhdGVkIHRoZSBhdmVyYWdlIG1lZGlhbiBhY3Jvc3MsIGFuZCB0aGUgbWlkZGxlIDkwJSBvZiB0aGUgZGF0YS4gCmBgYHtyfQpjdXJyX2RhdGEgPC0gbXVsdGlwbGVzICU+JSAKICB1bmdyb3VwKCkgJT4lCiAgc2VsZWN0KGxhbmcsIGRpcmVjdGlvbiwgeG1lZCwgeW1lZCwgeF85MCwgeV85MCkgJT4lCiAgZ3JvdXBfYnkobGFuZywgZGlyZWN0aW9uKSAlPiUKICBzdW1tYXJpc2UoeG1pbiA9IHhtZWQtKHhfOTAvMiksCiAgICAgICAgIHhtYXggPSB4bWVkKyh4XzkwLzIpLAogICAgICAgICB5bWluID0gLTEqKHltZWQtKHlfOTAvMikpLAogICAgICAgICB5bWF4ID0gLTEqKHltZWQrKHlfOTAvMikpKQoKZ2dwbG90KGN1cnJfZGF0YSwgYWVzKGZpbGw9ZGlyZWN0aW9uLGNvbG9yPWRpcmVjdGlvbikpICsKICBhbm5vdGF0aW9uX2N1c3RvbShnLCB4bWluPS1JbmYsIHhtYXg9SW5mLCB5bWluPS1JbmYsIHltYXg9SW5mKSArCiAgZ2VvbV9yZWN0KGFlcyh4bWluPXhtaW4seW1pbj15bWluLHhtYXg9eG1heCx5bWF4PXltYXgpLGFscGhhPS4xKSArIAogIHRoZW1lX2xpbmVkcmF3KCkgKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsMTQ0MCksIGV4cGFuZCA9IGMoMCwgMCkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygtMTA4MCwwKSwgZXhwYW5kID0gYygwLCAwKSkgKwogIGZhY2V0X3dyYXAoImxhbmciKQpgYGAKCgojIyDCsTEgU0QgKE1pZGRsZSA2OCUpClNvIHRoaXMgaXMgdXNpbmcgdGhlIG1lYW4gb2YgdGhlIG1lYW5zLCBwbHVzIG9yIG1pbnVzIG9uZSBTRC4gIFRoaXMgaXMgZXF1aXZhbGVudCB0byBtaWRkbGUgNjglLiAKYGBge3J9CmN1cnJfZGF0YSA8LSBtdWx0aXBsZXMgJT4lIAogIHVuZ3JvdXAoKSAlPiUKICBzZWxlY3QobGFuZywgZGlyZWN0aW9uLCB4X21lYW4sIHlfbWVhbiwgeF8xc2QsIHlfMXNkKSAlPiUKICBncm91cF9ieShsYW5nLCBkaXJlY3Rpb24pICU+JQogIHN1bW1hcmlzZSh4bWluID0geF9tZWFuLSh4XzFzZC8yKSwKICAgICAgICAgeG1heCA9IHhfbWVhbisoeF8xc2QvMiksCiAgICAgICAgIHltaW4gPSAtMSooeV9tZWFuLSh5XzFzZC8yKSksCiAgICAgICAgIHltYXggPSAtMSooeV9tZWFuKyh5XzFzZC8yKSkpCgpnZ3Bsb3QoY3Vycl9kYXRhLCBhZXMoZmlsbD1kaXJlY3Rpb24sY29sb3I9ZGlyZWN0aW9uKSkgKwogIGFubm90YXRpb25fY3VzdG9tKGcsIHhtaW49LUluZiwgeG1heD1JbmYsIHltaW49LUluZiwgeW1heD1JbmYpICsKICBnZW9tX3JlY3QoYWVzKHhtaW49eG1pbix5bWluPXltaW4seG1heD14bWF4LHltYXg9eW1heCksYWxwaGE9LjEpICsgCiAgdGhlbWVfbGluZWRyYXcoKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwxNDQwKSwgZXhwYW5kID0gYygwLCAwKSkgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKC0xMDgwLDApLCBleHBhbmQgPSBjKDAsIDApKSArCiAgZmFjZXRfd3JhcCgibGFuZyIpCmBgYAoKIyMgwrEyIFNEIChNaWRkbGUgOTYlKQpBbmQgdGhpcyBpcyB1c2luZyB0aGUgbWVhbiBvZiB0aGUgbWVhbnMsIHBsdXMgb3IgbWludXMgdHdvIFNELiAgVGhpcyBpcyBlcXVpdmFsZW50IHRvIG1pZGRsZSA5NiUuIApgYGB7cn0KY3Vycl9kYXRhIDwtIG11bHRpcGxlcyAlPiUgCiAgdW5ncm91cCgpICU+JQogIHNlbGVjdChsYW5nLCBkaXJlY3Rpb24sIHhfbWVhbiwgeV9tZWFuLCB4XzJzZCwgeV8yc2QpICU+JQogIGdyb3VwX2J5KGxhbmcsIGRpcmVjdGlvbikgJT4lCiAgc3VtbWFyaXNlKHhtaW4gPSB4X21lYW4tKHhfMnNkLzIpLAogICAgICAgICB4bWF4ID0geF9tZWFuKyh4XzJzZC8yKSwKICAgICAgICAgeW1pbiA9IC0xKih5X21lYW4tKHlfMnNkLzIpKSwKICAgICAgICAgeW1heCA9IC0xKih5X21lYW4rKHlfMnNkLzIpKSkKCmdncGxvdChjdXJyX2RhdGEsIGFlcyhmaWxsPWRpcmVjdGlvbixjb2xvcj1kaXJlY3Rpb24pKSArCiAgYW5ub3RhdGlvbl9jdXN0b20oZywgeG1pbj0tSW5mLCB4bWF4PUluZiwgeW1pbj0tSW5mLCB5bWF4PUluZikgKwogIGdlb21fcmVjdChhZXMoeG1pbj14bWluLHltaW49eW1pbix4bWF4PXhtYXgseW1heD15bWF4KSxhbHBoYT0uMSkgKyAKICB0aGVtZV9saW5lZHJhdygpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygwLDE0NDApLCBleHBhbmQgPSBjKDAsIDApKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoLTEwODAsMCksIGV4cGFuZCA9IGMoMCwgMCkpICsKICBmYWNldF93cmFwKCJsYW5nIikKYGBgCg==